#include "column.h"
#include "qtch/log.h"
#include "util.h"

namespace qtch {
namespace orm {

static Logger::ptr logger = QTCH_LOG_NAME("orm");


bool Column::init(const tinyxml2::XMLElement& node) {
    if(strcmp(node.Name(),"column") != 0){
        QTCH_LOG_ERROR(logger) << "parse error  it's not index node";
        return false;
    }

    if(!node.Attribute("name")){
        QTCH_LOG_ERROR(logger) << "column name not exists";
        return false;
    }
    m_name = node.Attribute("name");

    if(!node.Attribute("type")){
        QTCH_LOG_ERROR(logger) << "column name="<< m_name << " type not exists";
        return false;
    }
    m_type = node.Attribute("type");
    m_dtype = ParseType(m_type);
    if(m_dtype == TYPE_NULL){
        QTCH_LOG_ERROR(logger) << "column name="<< m_name << " type=" << m_type << " type not invalid";
        return false;
    }

    if(node.Attribute("desc")){
        m_desc = node.Attribute("desc");
    } 

    if(node.Attribute("update")){
        m_update = node.Attribute("update");
    }

    if(node.Attribute("default")){
        m_default = node.Attribute("default");
    }
    
    m_autoIncrement = node.BoolAttribute("auto_increment");

    m_length = node.IntAttribute("length");
    
    return true;

}

Column::Type Column::ParseType(const std::string& v) {
#define XX(a,b,c) \
    if(v == #b) { \
        return a; \
    } else if(v == #c){\
        return a;\
    }

    XX(TYPE_INT8, int8_t, int8);
    XX(TYPE_UINT8, uint8_t, uint8);
    XX(TYPE_INT16, int16_t, int16);
    XX(TYPE_UINT16, uint16_t, uint16);
    XX(TYPE_INT32, int32_t, int32);
    XX(TYPE_UINT32, uint32_t, uint32);
    XX(TYPE_INT64, int64_t, int64);
    XX(TYPE_UINT64, uint64_t, uint64);
    XX(TYPE_FLOAT, float, float);
    XX(TYPE_DOUBLE, double, double);
    XX(TYPE_STRING, string, std::string);
    XX(TYPE_TEXT, text, text);
    XX(TYPE_BLOB, blob, blob);
    XX(TYPE_TIMESTAMP, timestamp, datetime);

#undef XX
    return TYPE_NULL;
}

std::string Column::TypeToString(Type type) {
    switch(type){
#define XX(a,b) \
    case(a): \
        return #b;
    XX(TYPE_INT8, int8_t);
    XX(TYPE_UINT8, uint8_t);
    XX(TYPE_INT16, int16_t);
    XX(TYPE_UINT16, uint16_t);
    XX(TYPE_INT32, int32_t);
    XX(TYPE_UINT32, uint32_t);
    XX(TYPE_INT64, int64_t);
    XX(TYPE_UINT64, uint64_t);
    XX(TYPE_FLOAT, float);
    XX(TYPE_DOUBLE, double);
    XX(TYPE_STRING, std::string);
    XX(TYPE_TEXT, std::string);
    XX(TYPE_BLOB, std::string);
    XX(TYPE_TIMESTAMP, int64_t);
#undef XX
    default:
        return "null";
    }
}

std::string Column::getDefaultValueString() {
    if(m_default.empty()){
        return "";
    }
    if(m_dtype <= TYPE_DOUBLE){
        return m_default;
    }
    if(m_default == "current_timestamp"){
        return "time(0)";
    }
    return "qtch::Str2Time(\""+ m_default +"\")";
}

std::string Column::getMemberDefine() const {
    std::stringstream ss;
    ss << TypeToString(m_dtype) << " " << GetAsMemberName(m_name) << ";";
    return ss.str();
}

std::string Column::getGetFunDefine() const {
    std::stringstream ss;
    ss << "const " << TypeToString(m_dtype) << "& " << getAsGetFunName(m_name)
       << "() { return " << GetAsMemberName(m_name) << ";}";
    return ss.str();
}

std::string Column::getSetFunDefine() const {
    std::stringstream ss;
    ss << "void " << getAsSetFunName(m_name) << "(const " << TypeToString(m_dtype) << "& v);";
    return ss.str();
}

std::string Column::getSetFunImpl(const std::string& class_name) const {
    std::stringstream ss;
    ss << "void " << class_name << "::" << getAsSetFunName(m_name) << "(const " << TypeToString(m_dtype) << "& v){ \n"
       << "    " << GetAsMemberName(m_name) << " = v;\n"
       << "    " << GetAsMemberNameNull(m_name)  << " = false;\n"
       << "}";
    return ss.str();
}


std::string Column::getIsFunDefine() const {
    std::stringstream ss;
    ss << "const bool " << getAsIsFunName(m_name)
       << "() { return " << GetAsMemberNameNull(m_name) << ";}";
    return ss.str();
}

std::string Column::getSetNullDefine() const {
    std::stringstream ss;
    ss << "const bool " << getAsSetNullFunName(m_name)
       << "(bool v) { return " << GetAsMemberNameNull(m_name)  << " = v;}";
    return ss.str();
}


std::string Column::getBindString(Type type){
    switch(type){
#define XX(a, b) \
    case(a): \
        return "bind"#b;
    XX(TYPE_INT8, Int8);
    XX(TYPE_UINT8, Uint8);
    XX(TYPE_INT16, Int16);
    XX(TYPE_UINT16, Uint16);
    XX(TYPE_INT32, Int32);
    XX(TYPE_UINT32, Uint32);
    XX(TYPE_INT64, Int64);
    XX(TYPE_UINT64, Uint64);
    XX(TYPE_FLOAT, Float);
    XX(TYPE_DOUBLE, Double);
    XX(TYPE_STRING, String);
    XX(TYPE_TEXT, String);
    XX(TYPE_BLOB, Blob);
    XX(TYPE_TIMESTAMP, Time);
    XX(TYPE_NULL,  );
#undef XX
    default:
        return "";
    }
}

std::string Column::getBindString() {
    return getBindString(m_dtype);
}

std::string Column::getGetString(Type type){
    switch(type){
#define XX(a, b) \
    case(a): \
        return "get"#b;
    XX(TYPE_INT8, Int8);
    XX(TYPE_UINT8, Uint8);
    XX(TYPE_INT16, Int16);
    XX(TYPE_UINT16, Uint16);
    XX(TYPE_INT32, Int32);
    XX(TYPE_UINT32, Uint32);
    XX(TYPE_INT64, Int64);
    XX(TYPE_UINT64, Uint64);
    XX(TYPE_FLOAT, Float);
    XX(TYPE_DOUBLE, Double);
    XX(TYPE_STRING, String);
    XX(TYPE_TEXT, String);
    XX(TYPE_BLOB, Blob);
    XX(TYPE_TIMESTAMP, Time);
#undef XX
    default:
        return "";
    }
}

std::string Column::getGetString() {
    return getGetString(m_dtype);
}

std::string Column::toString() const {
    std::stringstream ss;
    dump(ss);
    return ss.str();
}

std::ostream& Column::dump(std::ostream& os) const {
    os << "[column name=" << m_name << " type=" << m_type << " default=" << m_default
       << " update=" << m_update << " desc=" << m_desc << " index=" << m_index 
       << " autoIncrement=" << m_autoIncrement << " dtype=" << m_dtype
       << " length=" << m_length << "]";
    return os;
}

std::ostream& operator<<(std::ostream& os, const Column& col) {
    col.dump(os);
    return os;
}

std::ostream& operator<<(std::ostream& os, const Column::ptr col) {
    col->dump(os);
    return os;
}

    


}
}